﻿using PropertyChanged;
using System;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using static TruckScale.Helpers.LogHelper;

namespace TruckScale.ViewModels
{
  public partial class MainViewModel
  {
    byte[] doCmd = { 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    byte[] doCmd2 = { 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    byte[] diCmd = { 0x01, 0x02, 0x00, 0x20, 0x00, 0x02, 0xF8, 0x01 };
    private bool barrier1 = false;
    private bool barrier2 = false;
    private bool light1 = false;
    private bool light2 = false;
    private SerialPort relaySP = new SerialPort(); //IO控制器串口
    private bool relaySpClosing; //正在关闭IO控制器串口的标识
    private bool relaySpListening; //IO控制器串口正在接收数据的标识
    private byte[] relaySpBuf;  //接收IO控制器串口数据的buffer
    private System.Timers.Timer diTimer;  //查询IO控制器开关量输入的定时器
    //命令发送失败则重复发送3次命令间隔1秒用到的变量
    private int _maxRetries = 3;
    private int _retryInterval = 1000; // 毫秒
    private ManualResetEvent _dataReceivedEvent = new ManualResetEvent(false);

    public bool Raster1 { get; set; }
    public bool Raster2 { get; set; }
    [DoNotCheckEquality]
    public bool Barrier1
    {
      get => barrier1;
      set
      {
        barrier1 = value;
        if (value)
        {
          RelayControl(2, 0);
        }
        else
        {
          RelayControl(3, 0);
        }
      }
    }
    [DoNotCheckEquality]
    public bool Barrier2
    {
      get => barrier2;
      set
      {
        barrier2 = value;
        if (value)
        {
          RelayControl(4, 0);
        }
        else
        {
          RelayControl(5, 0);
        }
      }
    }
    [DoNotCheckEquality]
    public bool Light1
    {
      get => light1;
      set
      {
        light1 = value;
        if (value)
        {
          RelayControl(6, 1);
        }
        else
        {
          RelayControl(6, 2);
        }
      }
    }
    [DoNotCheckEquality]
    public bool Light2
    {
      get => light2;
      set
      {
        light2 = value;
        if (value)
        {
          RelayControl(7, 1);
        }
        else
        {
          RelayControl(7, 2);
        }
      }
    }

    private void InitPeripheralsProps()
    {
      //检查是否启用IO控制器
      if (Global._hardwareConfig["IOEnable"] != "True")
      {
        return;
      }
      //打开IO控制器串口，若切换串口，要先关闭之前的，再重新打开新的
      if (relaySP.IsOpen)
      {
        relaySP.DataReceived -= RelaySP_DataReceived;
        relaySpClosing = true;
        while (relaySpListening) ;
        relaySP.Close();
        SetLog("关闭IO控制器串口:" + relaySP.PortName, LogType.HardwareLogger);
        relaySpClosing = false;
      }

      relaySP.PortName = Global._hardwareConfig["SelectedIOPort"];
      relaySP.BaudRate = 9600;
      relaySP.DataReceived += new SerialDataReceivedEventHandler(RelaySP_DataReceived);

      try
      {
        relaySP.Open();
        SetLog("打开IO控制器串口:" + relaySP.PortName, LogType.HardwareLogger);
      }
      catch (Exception e)
      {
        SetLog("IO控制器串口打开失败:" + e.Message, LogType.HardwareLogger);
        return;
      }

      //初始化查询IO控制器开关量输入定时器资源
      if (diTimer == null)
      {
        diTimer = new System.Timers.Timer
        {
          Interval = 1000
        };

        diTimer.Elapsed += (s, args) =>
        {
          if (relaySP.IsOpen)
          {
            relaySP.Write(diCmd, 0, diCmd.Length);
          }
        };
      }


      if (Global._hardwareConfig["GratingEnable"] == "True")
      {
        diTimer?.Start();
      }
      else
      {
        diTimer?.Stop();
      }
    }

    private void RelaySP_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
      if (relaySpClosing)
      {
        return;
      }

      try
      {
        relaySpListening = true;//设置标记，说明我已经开始处理数据。

        //获取串口Hex数据
        SerialPort sp = (SerialPort)sender;
        if (sp.PortName.Equals(relaySP.PortName))
        {
          relaySpBuf = new byte[sp.BytesToRead];
          sp.Read(relaySpBuf, 0, relaySpBuf.Length);
          Console.WriteLine(BitConverter.ToString(relaySpBuf));  //测试时打印，发布时可以去掉
          if (relaySpBuf[1] == 0x02) //开关量返回值
          {
            Raster1 = relaySpBuf[3] == 1 || relaySpBuf[3] == 3;
            Raster2 = relaySpBuf[3] == 2 || relaySpBuf[3] == 3;
          }
          if (relaySpBuf[1] == 0x05) //单个操作继电器的返回值
          {
            _dataReceivedEvent.Set(); // 设置信号，表示数据接收到
          }
        }
      }
      catch (Exception ex)
      {
        SetLog("IO控制器串口通信异常:" + ex.Message, LogType.HardwareLogger, LogLevel.Error);
      }
      finally
      {
        relaySpListening = false;//我用完了，ui可以关闭串口了。
      }
    }

    /// <summary>
    /// 操作IO控制器
    /// </summary>
    /// <param name="relayNo">IO控制器编号，取值0~7</param>
    /// <param name="mode">IO控制器触发模式：0 闭-开点动，1 闭合， 2 打开</param>
    private async void RelayControl(byte relayNo, int mode)
    {
      if (Global._hardwareConfig["GratingEnable"] == "True")
      {
        diTimer?.Stop();
      }

      try
      {
        byte relayState = mode == 1 ? (byte)0xFF : (byte)0x00;
        if (mode == 0)
        {
          // Toggle relay for mode 0
          await SendCommandToRelay(relayNo, 0xFF);
          await Task.Delay(300);
          relayState = 0x00;
        }

        await SendCommandToRelay(relayNo, relayState);
      }
      finally
      {
        if (Global._hardwareConfig["GratingEnable"] == "True")
        {
          diTimer?.Start();
        }
      }
    }

    /// <summary>
    /// 使用重发逻辑给继电器发送命令
    /// </summary>
    /// <param name="relayNo">IO控制器编号</param>
    /// <param name="relayState">继电器状态 (0xFF: 闭合, 0x00: 打开)</param>
    private async Task SendCommandToRelay(byte relayNo, byte relayState)
    {
      doCmd[3] = relayNo;
      doCmd[4] = relayState;
      CRCCalc(doCmd.Take(6).ToArray());

      if (!relaySP.IsOpen)
      {
        //Console.WriteLine("Serial port is not open.");
        return;
      }

      int retryCount = 0;
      while (retryCount < _maxRetries)
      {
        relaySP.Write(doCmd, 0, doCmd.Length);
        SetLog($"send -> {retryCount}/{_maxRetries} : {BitConverter.ToString(doCmd)}", LogType.HardwareLogger, LogLevel.Debug);
        _dataReceivedEvent.Reset(); // Reset the event for the next receive

        // Wait for data received event, with a timeout
        if (_dataReceivedEvent.WaitOne(_retryInterval))
        {
          //Console.WriteLine("Received response from device.");
          break; // Successfully received response, exit loop
        }
        else
        {
          retryCount++;
          //Console.WriteLine($"Retry {retryCount}/{_maxRetries}...");
        }
      }

      if (retryCount >= _maxRetries)
      {
        SetLog("Failed to receive response after maximum retries.", LogType.HardwareLogger, LogLevel.Error);
      }
    }

    //CRC校验
    private void CRCCalc(byte[] data)
    {
      byte[] crcbuf = data;
      //计算并填写CRC校验码
      int crc = 0xffff;
      int len = crcbuf.Length;
      for (int n = 0; n < len; n++)
      {
        byte i;
        crc = crc ^ crcbuf[n];
        for (i = 0; i < 8; i++)
        {
          int TT;
          TT = crc & 1;
          crc = crc >> 1;
          crc = crc & 0x7fff;
          if (TT == 1)
          {
            crc = crc ^ 0xa001;
          }
          crc = crc & 0xffff;
        }

      }
      doCmd[6] = (byte)(crc & 0xff);
      doCmd[7] = (byte)((crc >> 8) & 0xff);
    }
    //private string ByteArrayToHexString(byte[] data)
    //{
    //  StringBuilder sb = new StringBuilder(data.Length * 3);
    //  foreach (byte b in data)
    //    sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
    //  return sb.ToString().ToUpper();
    //}
  }
}
